package com.sikaryofficial.backend.manager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.sikaryofficial.backend.config.ProductConfig;
import com.sikaryofficial.backend.config.TenantFilterProperties;
import com.sikaryofficial.backend.constant.ProductConstant;
import com.sikaryofficial.backend.domain.dto.req.BrandTasteReq;
import com.sikaryofficial.backend.domain.dto.req.BrandTasteWithSpecialReq;
import com.sikaryofficial.backend.domain.dto.req.ProductDeleteReq;
import com.sikaryofficial.backend.domain.dto.req.ProductImageReq;
import com.sikaryofficial.backend.domain.dto.req.ProductListReq;
import com.sikaryofficial.backend.domain.dto.req.QrCodeDownloadListReq;
import com.sikaryofficial.backend.domain.dto.resp.ProductImageVO;
import com.sikaryofficial.backend.domain.dto.resp.QrCodeDownloadDTO;
import com.sikaryofficial.backend.domain.entity.BrandTaste;
import com.sikaryofficial.backend.domain.mapping.BrandTasteMapping;
import com.sikaryofficial.backend.domain.vo.export.ProductExportVO;
import com.sikaryofficial.backend.domain.vo.export.ProductImportVO;
import com.sikaryofficial.backend.mapper.BrandTasteMapper;
import com.sikaryofficial.backend.service.IAttachmentService;
import com.sikaryofficial.backend.service.IBrandTasteService;
import com.sikaryofficial.backend.service.address.Ip2LocationService;
import com.sikaryofficial.backend.service.brand.cache.BrandTasteCacheService;
import com.sikaryofficial.backend.service.user.UserCacheService;
import com.sikaryofficial.backend.utils.ProductUtil;
import com.sikaryofficial.common.core.exception.ServiceException;
import com.sikaryofficial.common.security.utils.SecurityUtils;
import com.sikaryofficial.system.api.model.product.BrandTasteDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StopWatch;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.sikaryofficial.backend.constant.ProductConstant.SOURCE_SYSTEM;
import static com.sikaryofficial.backend.utils.ProductUtil.buildProductMd5;
import static com.sikaryofficial.backend.utils.ProductUtil.getFieldByExcelProperty;

/**
 * @author : qinjinyuan
 * @desc : 产品管理
 * @date : 2023/10/30 15:39
 */
@Service
@Slf4j
@RefreshScope
@SuppressWarnings(value = "unchecked")
public class ProductManager {
    @Autowired
    private IBrandTasteService brandTasteService;
    @Autowired
    private IAttachmentService attachmentService;
    @Autowired
    private BrandTasteCacheService brandTasteCache;
    @Autowired
    private TenantFilterProperties tenantFilterProperties;
    @Autowired
    private BrandTasteMapper brandTasteMapper;
    @Autowired
    private Ip2LocationService ip2Location;
    @Resource
    private ProductConfig productConfig;
    @Autowired
    private UserCacheService userCacheService;

    /**
     * 处理导入数据
     *
     * @param excl
     * @param dataList
     */
    private void fillImportData(MultipartFile excl, List<BrandTasteDTO> dataList) throws Exception {
        List<ProductImportVO> productImportVOS = Lists.newArrayList();
        if (!excl.isEmpty()) {
            ExcelReader excelReader = ExcelUtil.getReader(excl.getInputStream());
            // 设置行处理器
            List<Map<String, Object>> allDataList = excelReader.readAll();
            // 默认从第1行开始
            int rowIndex = 1;
            for (Map<String, Object> data : allDataList) {
                ProductImportVO productImportVO = new ProductImportVO();
                productImportVOS.add(productImportVO);
                int finalRowIndex = rowIndex;
                // 遍历Excel数据到对应 productImportVO 的字段
                data.forEach((columnName, v) -> readExcelToVO(data, productImportVO, finalRowIndex, columnName));
                rowIndex++;
            }
        }
        Optional.of(productImportVOS).orElse(Lists.newArrayList()).forEach(item -> {
            BrandTasteDTO brandTasteDTO = new BrandTasteDTO();
            brandTasteDTO.setProductName(item.getProductNameEn());
            brandTasteDTO.setProductNameCn(item.getProductNameCn());
            brandTasteDTO.setModel(item.getModelEn());
            brandTasteDTO.setModelCn(item.getModelCn());
            // 强制转换为小写
            brandTasteDTO.setBrandNo(CharSequenceUtil.isNotBlank(item.getBrandNoEn()) ? item.getBrandNoEn().toUpperCase(Locale.ROOT) : null);
            brandTasteDTO.setBrandNoCn(item.getBrandNoCn());
            brandTasteDTO.setTenantId(tenantFilterProperties.getTenantByBrand(brandTasteDTO.getBrandNo()));
            // 强制转换为小写
            brandTasteDTO.setStyle(item.getStyleEn().toLowerCase(Locale.ROOT));
            brandTasteDTO.setStyleCn(item.getStyleCn());
            brandTasteDTO.setTasteName(item.getTasteNameEn());
            brandTasteDTO.setTasteNameCn(item.getTasteNameCn());
            brandTasteDTO.setTasteRemark(item.getTasteRemark());
            brandTasteDTO.setQrTitle(item.getQrTitleEn());
            brandTasteDTO.setVisible(item.getVisible());
            brandTasteDTO.setRemark(item.getProductIntroduceEn());
            brandTasteDTO.setRemarkCn(item.getProductIntroduceCn());
            brandTasteDTO.setQrMatlNo(item.getQrMatlNo());
            // md5字段设置-业务上建立唯一性
            brandTasteDTO.setMd5(buildProductMd5(brandTasteDTO.getBrandNo(), brandTasteDTO.getModel(), brandTasteDTO.getStyle(), brandTasteDTO.getTasteName()));
            dataList.add(brandTasteDTO);
        });
    }

    /**
     * 读取Excel数据到对应 productImportVO 的字段
     *
     * @param data            Excel非图片数据
     * @param productImportVO VO
     * @param finalRowIndex   行索引
     * @param columnName      字段名称
     */
    private void readExcelToVO(Map<String, Object> data, ProductImportVO productImportVO, int finalRowIndex, String columnName) {
        if (CharSequenceUtil.isBlank(columnName)) {
            return;
        }
        Field[] fields = ReflectUtil.getFields(ProductImportVO.class);
        // 匹配注解的字段及索引
        Field targetField = getFieldByExcelProperty(columnName, fields);
        if (Objects.isNull(targetField)) {
            return;
        }
        int columnIndex = targetField.getAnnotation(ExcelProperty.class).index();
        log.info("rowIndex={},columnIndex={}", finalRowIndex, columnIndex);
        Object value = data.get(columnName);
        log.info("data:{}", value);
        if (CharSequenceUtil.equals("用户可见", columnName)) {
            // 值转换
            value = "Y".equals(value) ? "0" : "1";
        }
        // 数据校验
        validExcelTxtValue(columnName, value, finalRowIndex, columnIndex);
        ProductUtil.setFieldValByReflect(productImportVO, columnIndex, value);
    }

    /**
     * Excel 文本值校验
     *
     * @param columnName
     * @param value
     */
    private void validExcelTxtValue(String columnName, Object value, int finalRowIndex, int columnIndex) {
        if (ProductConstant.EXCEL_VALID_COLUMN_NAME_INTRODUCE_EN.equals(columnName) && Objects.nonNull(value)) {
            String objStrValue = (String) value;
            if (CharSequenceUtil.isNotBlank(objStrValue) && objStrValue.length() > 1000) {
                throw new ServiceException("The product introduction field value is too long [standard len is 1000],RowIndex:" + finalRowIndex + " ColumnIndex:" + columnIndex);
            }
        }
    }

    /**
     * 产品列表
     *
     * @param req
     * @return
     */
    public IPage<BrandTasteDTO> productList(ProductListReq req) {
        Page<BrandTasteDTO> customerPage = new Page<>(req.getPageNum(), req.getPageSize());
        // 获取当前日期的开始时间
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startOfDay = LocalDateTimeUtil.beginOfDay(now);
        LocalDateTime threeDaysAgo = startOfDay.minus(productConfig.getAmountToSubtract(), ChronoUnit.DAYS);
        // 转换为 Date 类型
        Date startDate = Date.from(threeDaysAgo.atZone(ZoneId.systemDefault()).toInstant());
        Date endDate = Date.from(LocalDateTimeUtil.endOfDay(now).atZone(ZoneId.systemDefault()).toInstant());
        IPage<BrandTasteDTO> brandTastePage = brandTasteService.productPageList(customerPage, req, startDate, endDate);
        if (CollUtil.isEmpty(brandTastePage.getRecords())) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0);
        }
        // 扫描次数
        List<BrandTasteDTO> brandTasteDTOS = brandTastePage.getRecords();
        AtomicInteger rowNum = new AtomicInteger(1);
        Optional.of(brandTasteDTOS).ifPresent(list -> list.forEach(item -> {
            item.setSeqNo((req.getPageNum() - 1) * req.getPageSize() + rowNum.get());
            rowNum.getAndIncrement();
        }));
        return brandTastePage;
    }

    public IPage<QrCodeDownloadDTO> qrCodeDownloadList(QrCodeDownloadListReq req) {
        Page<QrCodeDownloadDTO> customerPage = new Page<>(req.getPageNum(), req.getPageSize());
        // 获取当前日期的开始时间
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startOfDay = LocalDateTimeUtil.beginOfDay(now);
        LocalDateTime threeDaysAgo = startOfDay.minus(productConfig.getAmountToSubtract(), ChronoUnit.DAYS);
        // 转换为 Date 类型
        Date startDate = Date.from(threeDaysAgo.atZone(ZoneId.systemDefault()).toInstant());
        Date endDate = Date.from(LocalDateTimeUtil.endOfDay(now).atZone(ZoneId.systemDefault()).toInstant());
        IPage<QrCodeDownloadDTO> brandTastePage = brandTasteService.qrCodeDownloadList(customerPage, req, startDate, endDate);
        if (CollUtil.isEmpty(brandTastePage.getRecords())) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0);
        }
        // 扫描次数
        List<QrCodeDownloadDTO> brandTasteDTOS = brandTastePage.getRecords();
        AtomicInteger rowNum = new AtomicInteger(1);
        Optional.of(brandTasteDTOS).ifPresent(list -> list.forEach(item -> {
            item.setSeqNo((req.getPageNum() - 1) * req.getPageSize() + rowNum.get());
            rowNum.getAndIncrement();
            item.setNickName(userCacheService.getSignUserNickName(item.getCreatedBy()));
        }));
        return brandTastePage;
    }

    /**
     * 导出产品
     *
     * @param req
     * @return
     */
    public List<ProductExportVO> productExport(ProductDeleteReq req) {
        if (CharSequenceUtil.isBlank(req.getBrandTasteIds())) {
            throw new ServiceException("Export BrandTasteIds is empty.");
        }
        List<String> brandTasteIdList = Arrays.asList(req.getBrandTasteIds().split(","));
        if (brandTasteIdList.size() > 500) {
            throw new ServiceException("Export BrandTasteIds size is bigger then 500.");
        }
        LambdaQueryWrapper<BrandTaste> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(BrandTaste::getDeletedVersion, 0L).eq(BrandTaste::getSource, SOURCE_SYSTEM).in(BrandTaste::getBrandTasteId, Arrays.asList(req.getBrandTasteIds().split(",")));
        List<BrandTaste> list = brandTasteService.list(lambdaQueryWrapper);
        List<ProductExportVO> result = BrandTasteMapping.INSTANCE.coverListToExportVO(list);
        int i = 1;
        for (ProductExportVO vo : result) {
            // 图片地址转换
            vo.setSeqNo(i);
            i++;
        }
        return result;
    }

    public List<ProductExportVO> productExport() {
        List<BrandTaste> list = brandTasteService.lambdaQuery().eq(BrandTaste::getSource, SOURCE_SYSTEM).eq(BrandTaste::getDeletedVersion, 0L)
                .orderByDesc(BrandTaste::getBrandTasteId).last(" limit 1 ").list();
        List<ProductExportVO> result = BrandTasteMapping.INSTANCE.coverListToExportVO(list);
        int i = 1;
        for (ProductExportVO vo : result) {
            vo.setSeqNo(i);
            i++;
        }
        return result;
    }

    /**
     * 导入产品
     *
     * @param excl
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void productImport(MultipartFile excl) {
        try {
            List<BrandTasteDTO> dataList = Lists.newArrayList();
            // 1、 把Excel转为java对象的导入数据
            fillImportData(excl, dataList);
            // 2、导入数据入库处理
            processImportData(dataList);
        } catch (Exception e) {
            log.error("导入数据异常。{}", e);
            throw new ServiceException("product import error." + e.getMessage());
        }
    }

    /**
     * 导入数据处理
     *
     * @param brandTasteDTOS
     * @return
     */
    private void processImportData(List<BrandTasteDTO> brandTasteDTOS) {
        // 导入数据处理
        // 分析哪些数据是新增 or 修改 or 重复
        List<String> md5List = brandTasteDTOS.stream().map(BrandTasteDTO::getMd5).filter(Objects::nonNull).collect(Collectors.toList());
        List<BrandTaste> brandTasteListDB = brandTasteService.list(new LambdaQueryWrapper<BrandTaste>().eq(BrandTaste::getDeletedVersion, 0L).eq(BrandTaste::getSource, SOURCE_SYSTEM).in(BrandTaste::getMd5, md5List));
        // 获取待更新和待新增数据
        List<BrandTasteDTO> brandTasteUpdateList = Lists.newArrayList();
        List<BrandTasteDTO> brandTasteInsertList;
        Map<String, BrandTaste> brandTasteMd5Map = brandTasteListDB.stream().collect(Collectors.toMap(BrandTaste::getMd5, Function.identity(), (key1, key2) -> key2));
        if (CollUtil.isNotEmpty(brandTasteListDB)) {
            brandTasteUpdateList = brandTasteDTOS.stream().filter(item -> brandTasteMd5Map.containsKey(item.getMd5())).collect(Collectors.toList());
            brandTasteInsertList = brandTasteDTOS.stream().filter(item -> !brandTasteMd5Map.containsKey(item.getMd5())).collect(Collectors.toList());
        } else {
            brandTasteInsertList = brandTasteDTOS;
        }
        // 数据批量更新和插入
        insertBrandTastes(brandTasteInsertList);
        // 封装待更新数据的主键
        updateBrandTastes(brandTasteUpdateList, brandTasteMd5Map);
    }

    /**
     * 处理更新产品
     *
     * @param brandTasteUpdateList
     * @param brandTasteMd5Map
     * @return
     */
    private List<BrandTaste> updateBrandTastes(List<BrandTasteDTO> brandTasteUpdateList, Map<String, BrandTaste> brandTasteMd5Map) {
        if (CollUtil.isEmpty(brandTasteUpdateList)) {
            return Lists.newArrayList();
        }
        List<BrandTaste> entityUpdateList = BrandTasteMapping.INSTANCE.coverToEntityList(brandTasteUpdateList);
        entityUpdateList.forEach(item -> {
            item.setBrandTasteId(brandTasteMd5Map.get(item.getMd5()).getBrandTasteId());
            item.setUpdatedBy(SecurityUtils.getUserId());
            item.setSource(SOURCE_SYSTEM);
            item.setUpdatedTime(new Date());
        });
        brandTasteService.saveOrUpdateBatch(entityUpdateList);
        // 更新缓存数据
        for (BrandTaste entity : entityUpdateList) {
            BrandTasteDTO dto = BrandTasteMapping.INSTANCE.coverToDTO(entity);
            BrandTaste dbData = brandTasteMd5Map.get(dto.getMd5());
            // 补充字段
            dto.setCompositeScore(dbData.getCompositeScore());
            dto.setCreatedTime(dbData.getCreatedTime());
            dto.setCreatedBy(dbData.getCreatedBy());
            dto.setCreatedName(dbData.getCreatedName());
            dto.setQrExportNum(dbData.getQrExportNum());
            dto.setProductShortId(dbData.getProductShortId());
            // 补充扩展数据
            brandTasteCache.updateBrandTasteCache(dto);
        }
        return entityUpdateList;
    }

    /**
     * 处理新增产品
     *
     * @param brandTasteInsertList
     * @return
     */
    private List<BrandTaste> insertBrandTastes(List<BrandTasteDTO> brandTasteInsertList) {
        if (CollUtil.isEmpty(brandTasteInsertList)) {
            return Lists.newArrayList();
        }
        List<BrandTaste> entityInsertList = BrandTasteMapping.INSTANCE.coverToEntityList(brandTasteInsertList);
        Integer maxShortId = brandTasteService.getMaxShortId();
        AtomicInteger rowNum = new AtomicInteger(maxShortId);
        entityInsertList.forEach(item -> {
            item.setCreatedBy(SecurityUtils.getUserId());
            item.setCreatedTime(new Date());
            item.setBrandTasteId(IdWorker.getId());
            // 增加source字段，默认为0
            item.setSource(SOURCE_SYSTEM);
            // 新增数据，序列号自增
            item.setProductShortId(rowNum.getAndIncrement());
        });
        brandTasteService.saveOrUpdateBatch(entityInsertList);
        // 更新缓存数据
        for (BrandTaste entity : entityInsertList) {
            BrandTasteDTO dto = BrandTasteMapping.INSTANCE.coverToDTO(entity);
            // 补充扩展数据
            brandTasteCache.updateBrandTasteCache(dto);
        }
        return entityInsertList;
    }



    /**
     * 设置可见性
     *
     * @param brandTasteId 品类id
     * @param visible      可见 0-可见 1-不可见;默认0
     * @return
     */
    public boolean setVisible(Long brandTasteId, Integer visible) {
        boolean flag = brandTasteService.update(new BrandTaste().setVisible(visible), new LambdaQueryWrapper<BrandTaste>().eq(BrandTaste::getBrandTasteId, brandTasteId).eq(BrandTaste::getDeletedVersion, 0L).eq(BrandTaste::getSource, SOURCE_SYSTEM));
        // 缓存数据更新
        BrandTasteDTO brandTasteDTO = brandTasteCache.getSignBrandTaste(brandTasteId);
        brandTasteDTO.setVisible(visible);
        brandTasteCache.updateBrandTasteCacheOnly(brandTasteDTO);
        return flag;
    }

    /**
     * 品牌专栏-产品列表
     *
     * @param req
     * @return
     */
    public IPage<BrandTasteDTO> listWithBrandSpecial(BrandTasteWithSpecialReq req) {
        Integer tenantId = tenantFilterProperties.getTenantByBrand(req.getBrandNo());
        if (Objects.isNull(tenantId)) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0);
        }
        Page<BrandTasteDTO> customerPage = new Page<>(req.getPageNum(), req.getPageSize());
        BrandTasteReq brandTasteReq = new BrandTasteReq();
        brandTasteReq.setBrandNo(req.getBrandNo());
        // 查询可见数据
        brandTasteReq.setVisible(0);
        brandTasteReq.setSeries(req.getSeries());
        try {
            // 增加国家控制 -- 根据IP 获取国家，然后根据国家过滤 产品
            String isoCountryCode = ip2Location.getISOCountryCode();
            log.info("==================ISOCountryCode===={}", isoCountryCode);
            brandTasteReq.setCountryCode(isoCountryCode);
        } catch (Exception e) {
            log.error("------------{}", e);
        }
        return brandTasteMapper.getBrandTasteList(customerPage, brandTasteReq);
    }

    /**
     * 保存产品图片
     * @param req
     */
    public void saveProductImage(ProductImageReq req) {
        if (StrUtil.isBlank(req.getCoverUrl())) {
            return;
        }
        // 更新产品图片
        // TODO: 检查下这里的执行时间  。。。。
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        brandTasteService.lambdaUpdate().eq(BrandTaste::getBrandTasteId, req.getBrandTasteId())
                .set(BrandTaste::getCoverUrl, req.getCoverUrl())
                .set(BrandTaste::getUpdatedBy, SecurityUtils.getUserId())
                .set(BrandTaste::getUpdatedTime, new Date())
                .update();
        stopWatch.stop();
        log.info("update brandTaste coverUrl cost: {} ms", stopWatch.getTotalTimeMillis());
        if (CollUtil.isEmpty(req.getAttachmentList())) {
            return;
        }
        // 删除产品图片
        stopWatch.start();
        attachmentService.deleteAttachmentList(req.getBrandTasteId(), SecurityUtils.getUserId());
        stopWatch.stop();
        log.info("delete brandTaste attachmentList cost: {} ms", stopWatch.getTotalTimeMillis());
        // 保存产品图片
        stopWatch.start();
        attachmentService.saveAttachmentList(req.getBrandTasteId(), req.getAttachmentList(),
                SecurityUtils.getUserId());
        stopWatch.stop();
        log.info("save brandTaste attachmentList cost: {} ms", stopWatch.getTotalTimeMillis());

        // 缓存数据更新
        BrandTasteDTO brandTasteDTO = brandTasteCache.getSignBrandTaste(req.getBrandTasteId());
        brandTasteDTO.setCoverUrl(req.getCoverUrl());
        brandTasteCache.updateBrandTasteCacheOnly(brandTasteDTO);
    }

    public ProductImageVO viewProductImage(Long brandTasteId) {
        BrandTaste brandTaste =  brandTasteService.getById(brandTasteId);
        return ProductImageVO.builder()
                .brandTasteId(brandTasteId)
                .productName(brandTaste.getProductName())
                .brandNo(brandTaste.getBrandNo())
                .model(brandTaste.getModel())
                .tasteName(brandTaste.getTasteName())
                .style(brandTaste.getStyle())
                .remark(brandTaste.getRemark())
                .qrMatlNo(brandTaste.getQrMatlNo())
                .coverUrl(brandTaste.getCoverUrl())
                .attachmentList(attachmentService.getAttachmentById(brandTasteId))
                .build();
    }
}
